package com.kingtao.rpc;

import com.kingtao.entry.LogEntry;
import com.kingtao.entry.Node;
import com.kingtao.entry.NodeGroup;
import com.kingtao.entry.Peer;
import com.kingtao.enums.NodeStatusEnums;
import com.kingtao.message.*;
import com.kingtao.service.Impl.LogServiceImpl;
import com.kingtao.service.Impl.NodeGroupServiceImpl;
import com.kingtao.session.GroupSessionImpl;
import com.kingtao.util.CoreData;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

/**
 * @Author: kingtao
 * @Date: 2023-08-31-11:35
 * @Description:
 */
@Slf4j
public class TestServerManger {
    //  控制并发线程数目
    private static final int THREAD_COUNT = 30;
    private static ExecutorService threadPool = Executors.newFixedThreadPool(THREAD_COUNT);
    private static Semaphore semaphore = new Semaphore(10);

    public static List<Node> nodes = new ArrayList<>();
    public static NodeGroup nodegroup = new NodeGroup();
    public static NodeGroupServiceImpl groupService = new NodeGroupServiceImpl();
    public static GroupSessionImpl groupSession = new GroupSessionImpl();
    public static LogEntry logEntry = new LogEntry();
    public static List<LogEntry> logEntries;

    static int leaderId = 0;
    static int secondLeaderId = 0;

    public static void runServer() {
        Scanner scanner = new Scanner(System.in);
        String s;
        while (true) {
            System.out.println("############输入命令#############");
            System.out.println("1、初始化共识(默认" + CoreData.NODE_NUM + "个节点)");
            System.out.println("2、新增节点（在默认基础上追加）");
            System.out.println("3、发送请求");
            System.out.println("4、开启分组管理（组数:"+ CoreData.GROUP_NUM +"）");
            System.out.println("5、集群管理信息");
            System.out.println("6、leader作恶");
            System.out.println("7、secondLeader作恶");
            System.out.println("################################");
            System.out.println("输入指令：");

            s = scanner.nextLine();
            switch (s) {
                case "1":
                    Init();
                    groupService.setLeader();
                    groupService.setSecondLeader();
                    leaderId = groupService.getLeader().getId();
                    secondLeaderId = groupService.getSecondLeader().getId();
                    for (int i = 0; i < groupSession.getChannelNum(); i++) {
                        groupSession.getChannel(i)
                                .writeAndFlush(new ServerInitRequest(leaderId, secondLeaderId, groupService.getNodeGroup()));
                    }
                    //  开启分组
//                    execGroup();
                    break;
                case "2":
                    System.out.println("输入新增节点数目：");
                    String num = scanner.nextLine();
                    int size = 5;
                    if (groupService.getNodeGroup() != null) {
                        size = groupService.getNodeGroup().getNodes().size();
                    }
                    for (int i = 0; i < Integer.parseInt(num); i++) {
                        TestClient client = new TestClient();
                        Channel channel = client.clientInit();
                        int id = i + size;
                        groupSession.bind(channel, id);
                        Node node = new Node(id, new Peer(CoreData.NODE_ADDRESS, CoreData.NODE_INIT_PORT + id));
                        node.setLeaderCurrentTerm(nodegroup.getLeaderCurrentTerm());
                        node.setSecondLeaderCurrentTerm(nodegroup.getSecondLeaderCurrentTerm());
                        nodes.add(node);
                    }
                    for (int i = 0; i < Integer.parseInt(num); i++) {
                        Channel channel = groupSession.getChannel(i + size);
                        if (channel != null) {
                            channel.writeAndFlush(new AppendNodeResponse(i + size, true));
                        } else {
                            channel.writeAndFlush(new AppendNodeResponse(i + size, false));
                        }
                    }
                    break;
                case "3":
                    System.out.println("输入需要记录的请求内容：");
                    String str = scanner.nextLine();
                    logEntry.setValue(str);
                    allWrite();
                    break;
                case "4":
                    execGroup();
                    break;
                case "5":
                    System.out.println("集群信息：");
                    log.debug("{}", groupService.getNodeGroup());
                    break;
                case "6":
                    log.debug("leader 作恶");
                    groupService.punishNode(leaderId);
                    leaderId = groupService.getLeader().getId();
                    secondLeaderId = groupService.getSecondLeader().getId();
                    for (int i = 0; i < groupService.getNodeGroup().getNodes().size(); i++) {
                        groupSession.getChannel(i).writeAndFlush(new ServerInitRequest(leaderId, secondLeaderId, groupService.getNodeGroup()));
                    }
                    break;
                case "7":
                    log.debug("secondLeader 作恶");
                    groupService.punishNode(secondLeaderId);
                    secondLeaderId = groupService.getSecondLeader().getId();
                    for (int i = 0; i < groupService.getNodeGroup().getNodes().size(); i++) {
                        groupSession.getChannel(i).writeAndFlush(new ServerInitRequest(leaderId, secondLeaderId, groupService.getNodeGroup()));
                    }
                    break;
            }
        }
    }

    private static void allWrite() {
        //  leader、followerLeader 写入
        boolean write = leaderWrite(logEntry);
        //  分组
        //  followerLeader 存在 followerLeader写入
        if (groupService.isCreateGroup()) {
            if (write) {
//                            followerLeaderMapWrite(groupService.getNodeGroup().getFollowerLeaderIdMap(), logEntry);
                List<Node> allFollowerLeader = groupService.getNodeGroup().getAllFollowerLeader();
                for (Node followerLeader : allFollowerLeader ) {
                    threadPool.execute(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                semaphore.acquire();
                                followerLeaderWrite(logEntry, followerLeader);
                                semaphore.release();
                            }catch (InterruptedException e){
                                log.debug("err: {}", e.getMessage());
                            }
                        }
                    });
//                                new Thread(()->{
//                                    followerLeaderWrite(logEntry, followerLeader);
//                                }).start();
                }
            }
        } else {
            //  未分组
            if (write) {
                for (Node follower : groupService.getNodeGroup().getAllFollower()) {
//                                new Thread(()->{
//                                    followerWrite(logEntry, follower);
//                                }).start();

//                                followerWrite(logEntry, follower);
                    threadPool.execute(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                semaphore.acquire();
                                followerWrite(logEntry, follower);
                                semaphore.release();
                            }catch (InterruptedException e){
                                log.debug("err: {}", e.getMessage());
                            }
                        }
                    });
                }
//                            threadPool.shutdown();
            }
        }
    }

    private static void execGroup() {
        log.debug("启动分组");
        if (groupService.getNodeGroup().getNodes().size() < CoreData.GROUP_NODE_MIN_NUM) {
            log.debug("节点数目太少无法分组");
            log.debug("{}", groupService.getNodeGroup());
        } else {
            groupService.setCreateGroup(true);
            groupService.setFollowerLeader();
            Map<Integer, Integer> followerLeaderIdMap = groupService.getNodeGroup().getFollowerLeaderIdMap();
            for (int i = 0; i < CoreData.GROUP_NUM; i++) {
                groupSession.getChannel(followerLeaderIdMap.get(i))
                        .writeAndFlush(new GroupServiceRequest(followerLeaderIdMap.get(i), "followerleader"));
                log.debug("第{}组，组长节点：node {}", i % CoreData.GROUP_NUM + 1, followerLeaderIdMap.get(i));
            }
        }
    }

    private static void Init() {
        //  初始化节点
        for (int i = 0; i < CoreData.NODE_NUM; i++) {
            TestClient client = new TestClient();
            Channel channel = client.clientInit();
            groupSession.bind(channel, i);
            Node node = new Node(i, new Peer(CoreData.NODE_ADDRESS, CoreData.NODE_INIT_PORT + i));
            nodes.add(node);
            log.debug("node {}初始化", i);
        }

        nodegroup.setNodes(nodes);
        groupService.setNodeGroup(nodegroup);
    }

    //  节点写入
    public static boolean leaderWrite(LogEntry logEntry) {
        LogServiceImpl logService = new LogServiceImpl(groupService.getLeader());
        logService.writeLogEntry(logEntry);
        int lastIndex = logService.getLastIndex();
        //  验证
        if (logService.getValueFromIndexByKey(lastIndex, 1).equals(logEntry.getValue())) {
            //  奖励
            groupService.rewardNode(groupService.getLeader().getId());
            log.debug("Node {} ,状态：{}, 信誉值：{}, 写入成功",
                    groupService.getLeader().getId(),
                    groupService.getLeader().getStatus(),
                    groupService.getLeader().getCredit());
            //  更新 node 存储的 lastIndex
            groupService.getLeader().setLastLogIndex(lastIndex);
            logEntries = logService.getAllLogEntry();
            logService.destroy();
            groupSession.getChannel(groupService.getLeader().getId()).writeAndFlush(new AppendLeaderLogEntryResponse("leader日志写入成功", true));
            return secondLeaderWrite(logEntry);
        } else {
            groupSession.getChannel(groupService.getLeader().getId()).writeAndFlush(new AppendLeaderLogEntryResponse("leader日志写入失败", false));
            //  作恶，上次记录不算
            logService.destroy();
            boolean flag = true;
            while (flag) {
                log.debug("leader 不诚实, 重新筛选");
                //  leader换人了
                groupService.punishNode(groupService.getLeader().getId());
                //  重新筛选
                groupService.setLeader();
                LogServiceImpl newLogService = new LogServiceImpl(groupService.getLeader());
                newLogService.updateLastIndexKey(lastIndex - 1);
                newLogService.writeLogEntry(logEntry);
                groupService.rewardNode(groupService.getLeader().getId());
                if (newLogService.getValueFromIndexByKey(logService.getLastIndex(), 1).equals(logEntry.getValue())) {
                    flag = false;
                    lastIndex = newLogService.getLastIndex();
                    logEntries = newLogService.getAllLogEntry();
                    groupService.getLeader().setLastLogIndex(lastIndex);
                    groupService.rewardNode(groupService.getLeader().getId());
                    groupSession.getChannel(groupService.getLeader().getId()).writeAndFlush(new ClientResponse(true));
                    return secondLeaderWrite(logEntry);
                }
                newLogService.destroy();
            }
        }
        return false;
    }

    //  secondLeader 写入
    public static boolean secondLeaderWrite(LogEntry logEntry) {
        LogServiceImpl logService = new LogServiceImpl(groupService.getSecondLeader());
        if (logService.getLastIndex() == groupService.getLeader().getLastLogIndex() - 1) {
            logService.writeLogEntry(logEntry);
            int lastIndex = logService.getLastIndex();
            //  验证
            if (logService.getValueFromIndexByKey(lastIndex, 1).equals(logEntry.getValue())) {
                //  奖励
                groupService.rewardNode(groupService.getSecondLeader().getId());
                log.debug("Node {} ,状态：{}, 信誉值：{}, 写入成功",
                        groupService.getSecondLeader().getId(),
                        groupService.getSecondLeader().getStatus(),
                        groupService.getSecondLeader().getCredit());
                //  更新 node 存储的 lastIndex
                groupService.getSecondLeader().setLastLogIndex(lastIndex);
                logEntries = logService.getAllLogEntry();
                logService.destroy();
                groupSession.getChannel(groupService.getSecondLeader().getId()).writeAndFlush(new AppendSecondLeaderLogEntryResponse("SecondLeader 写入成功", true));
                return true;
            } else {
                groupSession.getChannel(groupService.getSecondLeader().getId()).writeAndFlush(new AppendLeaderLogEntryResponse("SecondLeader 日志写入失败", false));
                //  作恶，上次记录不算
                logService.destroy();
                boolean flag = true;
                while (flag) {
                    log.debug("SecondLeader 不诚实, 重新筛选");
                    //  SecondLeader
                    groupService.punishNode(groupService.getSecondLeader().getId());
                    //  重新筛选
                    groupService.setLeader();
                    LogServiceImpl newLogService = new LogServiceImpl(groupService.getSecondLeader());
                    newLogService.updateLastIndexKey(lastIndex - 1);
                    newLogService.writeLogEntry(logEntry);
                    groupService.rewardNode(groupService.getSecondLeader().getId());
                    if (newLogService.getValueFromIndexByKey(logService.getLastIndex(), 1).equals(logEntry.getValue())) {
                        flag = false;
                        lastIndex = newLogService.getLastIndex();
                        logEntries = newLogService.getAllLogEntry();
                        groupService.getLeader().setLastLogIndex(lastIndex);
                        groupService.rewardNode(groupService.getSecondLeader().getId());
                        groupSession.getChannel(groupService.getSecondLeader().getId()).writeAndFlush(new ClientResponse(true));
                        newLogService.destroy();
                        return true;
                    }
                }
            }
        } else {
            logService.updateAllLog(logEntries);
            groupService.getSecondLeader().setLastLogIndex(logEntries.size());
            logService.destroy();
            return true;
        }
        return false;
    }

    //  followerLeaderMap 写入
    public static void followerLeaderMapWrite(Map<Integer, Integer> map, LogEntry logEntry) {
        for (int i = 0; i < map.size(); i++) {
            int followerLeaderId = map.get(i);
            log.debug("followerLeaderId: {}", followerLeaderId);
            Node followerLeader = groupService.getNodeGroup().getNodeByNodeId(followerLeaderId);
            new Thread(() -> {
                log.debug("followerLeaderMap");
                followerLeaderWrite(logEntry, followerLeader);
            }, "followerLeader-" + i + "写入").start();
        }
    }

    //  followerLeader 写入
    public static void followerLeaderWrite(LogEntry logEntry, Node node) {
        if (node.getStatus() == NodeStatusEnums.followerLeader) {
            LogServiceImpl logService = new LogServiceImpl(node);
            if (logService.getLastIndex() == groupService.getSecondLeader().getLastLogIndex() - 1) {
                logService.writeLogEntry(logEntry);
                int lastIndex = logService.getLastIndex();
                //  验证
                if (logService.getValueFromIndexByKey(lastIndex, 1).equals(logEntry.getValue())) {
                    //  奖励
                    groupService.rewardNode(node.getId());
                    log.debug("Node {} ,状态：{}, 信誉值：{}, 写入成功",
                            node.getId(),
                            groupService.getNodeGroup().getNodeByNodeId(node.getId()).getStatus(),
                            groupService.getNodeGroup().getNodeByNodeId(node.getId()).getCredit());
                    //  更新 node 存储的 lastIndex
                    groupService.getNodeGroup().getNodeByNodeId(node.getId()).setLastLogIndex(lastIndex);
                    logEntries = logService.getAllLogEntry();
                    logService.destroy();
                    groupSession.getChannel(node.getId()).writeAndFlush(new AppendFollowLeaderLogEntryResponse("followerLeader 写入成功", true));
                    for (int i = node.getId() % CoreData.GROUP_NUM; i < groupService.getNodeGroup().getNodes().size(); i = i + CoreData.GROUP_NUM) {
                        followerWrite(logEntry, groupService.getNodeGroup().getNodeByNodeId(i));
                    }
                } else {
                    groupSession.getChannel(node.getId()).writeAndFlush(new AppendFollowLeaderLogEntryResponse("followerLeader 日志写入失败", false));
                    //  作恶，上次记录不算
                    logService.updateLastIndexKey(lastIndex - 1);
                    logService.destroy();
                    boolean flag = true;
                    while (flag) {
                        log.debug("followerLeader 不诚实, 重新筛选");
                        //  SecondLeader
                        groupService.punishNode(node.getId());
                        //  重新筛选
                        groupService.setFollowerLeaderById(node.getId());
                        Map<Integer, Integer> followerLeaderIdMap = groupService.getNodeGroup().getFollowerLeaderIdMap();
                        Integer followerLeaderId = followerLeaderIdMap.get(node.getId() % CoreData.GROUP_NUM);
                        Node followerLeader = groupService.getNodeGroup().getNodeByNodeId(followerLeaderId);
                        LogServiceImpl newLogService = new LogServiceImpl(followerLeader);
                        newLogService.writeLogEntry(logEntry);
                        groupService.rewardNode(groupService.getSecondLeader().getId());
                        if (newLogService.getValueFromIndexByKey(logService.getLastIndex(), 1).equals(logEntry.getValue())) {
                            flag = false;
                            lastIndex = newLogService.getLastIndex();
                            logEntries = newLogService.getAllLogEntry();
                            followerLeader.setLastLogIndex(lastIndex);
                            groupService.rewardNode(groupService.getSecondLeader().getId());
                            groupSession.getChannel(groupService.getSecondLeader().getId()).writeAndFlush(new ClientResponse(true));
                            newLogService.destroy();
                        }
                        newLogService.destroy();
                    }
                }
            } else {
                logService.updateAllLog(logEntries);
                groupService.getNodeGroup().getNodeByNodeId(node.getId()).setLastLogIndex(logEntries.size());
                log.debug("followerLeader 日志追加成功");
                logService.destroy();
            }

//            for (int i = node.getId() % CoreData.GROUP_NUM; i < groupService.getNodeGroup().getNodes().size(); i = i + CoreData.GROUP_NUM) {
//                Node follower = groupService.getNodeGroup().getNodeByNodeId(i);
//                if (follower.getStatus() == NodeStatusEnums.follower) {
////                followerWrite(logEntry, follower);
//                    new Thread(() -> {
//                        followerWrite(logEntry, follower);
//                    }).start();
//                }
//            }
        }
    }

    //  follower 写入
    public static void followerWrite(LogEntry logEntry, Node node) {
        if(node.getStatus() == NodeStatusEnums.follower) {
            LogServiceImpl logService = new LogServiceImpl(node);
//            log.debug("logService.getLastIndex() : {}", logService.getLastIndex());
//            log.debug("groupService.getSecondLeader().getLastLogIndex() : {}", groupService.getSecondLeader().getLastLogIndex());
            if (logService.getLastIndex() == groupService.getSecondLeader().getLastLogIndex() - 1) {
                logService.writeLogEntry(logEntry);
                int lastIndex = logService.getLastIndex();
                //  验证
                if (logService.getValueFromIndexByKey(lastIndex, 1).equals(logEntry.getValue())) {
                    //  奖励
                    groupService.rewardNode(node.getId());
                    //  更新 node 存储的 lastIndex
                    groupService.getNodeGroup().getNodeByNodeId(node.getId()).setLastLogIndex(lastIndex);
                    logEntries = logService.getAllLogEntry();
                    groupSession.getChannel(node.getId()).writeAndFlush(new AppendFollowerLogEntryResponse("node" + node.getId() + "写入成功", true));
                    log.debug("Node {} ,状态：{}, 信誉值：{}, 写入成功",
                            node.getId(),
                            groupService.getNodeGroup().getNodeByNodeId(node.getId()).getStatus(),
                            groupService.getNodeGroup().getNodeByNodeId(node.getId()).getCredit());
                } else {
                    groupSession.getChannel(groupService.getSecondLeader().getId()).writeAndFlush(new AppendFollowerLogEntryResponse("Node" + node.getId() + "日志写入失败", false));
                    boolean flag = true;
                    while (flag) {
                        log.debug("node {} 不诚实, 重新筛选", node.getId());
                        groupService.punishNode(node.getId());
                        logService.updateLastIndexKey(logService.getLastIndex() - 1);
                        logService.writeLogEntry(logEntry);
                        //  验证
                        if (logService.getValueFromIndexByKey(lastIndex, 1).equals(logEntry.getValue())) {
                            //  奖励
                            groupService.rewardNode(node.getId());
                            //  更新 node 存储的 lastIndex
                            groupService.getNodeGroup().getNodeByNodeId(node.getId()).setLastLogIndex(lastIndex);
                            flag = false;
                            groupSession.getChannel(node.getId()).writeAndFlush(new AppendFollowerLogEntryResponse("node" + node.getId() + "写入成功", true));
                            log.debug("Node {} ,状态：{}, 信誉值：{}, 写入成功",
                                    node.getId(),
                                    groupService.getNodeGroup().getNodeByNodeId(node.getId()).getStatus(),
                                    groupService.getNodeGroup().getNodeByNodeId(node.getId()).getCredit());
                        }
                    }
                }
            } else {
                logService.updateAllLog(logEntries);
                groupService.getNodeGroup().getNodeByNodeId(node.getId()).setLastLogIndex(logEntries.size());
                groupSession.getChannel(node.getId()).writeAndFlush(new AppendFollowerLogEntryResponse("node" + node.getId() + "追加成功", true));
                log.debug("Node {} ,状态：{}, 信誉值：{}, 追加成功",
                        node.getId(),
                        groupService.getNodeGroup().getNodeByNodeId(node.getId()).getStatus(),
                        groupService.getNodeGroup().getNodeByNodeId(node.getId()).getCredit());
            }
            logService.destroy();
        }
    }

    public static void main(String[] args) {
        runServer();
    }
}
